package com.primeton.poctag.util;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;


/**
 *
 * 封装 RestApi 调用
 *
 *
 * <pre>
 *
 * Created by zhaopx.
 * User: zhaopx
 * Date: 2020/4/3
 * Time: 17:38
 *
 * </pre>
 *
 * @author zhaopx
 */
public class RestApiUtils {


    static Logger log = LoggerFactory.getLogger(RestApiUtils.class);

    /**
     * http client
     */
    static final CloseableHttpClient httpClient;


    /**
     * Cookie store
     */
    private final static BasicCookieStore cookieStore = new BasicCookieStore();

    static {
        try {
            //设置协议http和https对应的处理socket链接工厂的对象
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", new SSLConnectionSocketFactory(createIgnoreVerifySSL()))
                    .build();

            PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            httpClient = HttpClients.custom()
                    .setConnectionManager(cm)
                    .setDefaultCookieStore(cookieStore)
                    .setDefaultRequestConfig(RequestConfig.custom().setConnectTimeout(180000).build())
                    .setRetryHandler(new DefaultHttpRequestRetryHandler(3, false))
                    .build();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }



    /**
     * 绕过验证
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sc = SSLContext.getInstance("SSLv3");

        // 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sc.init(null, new TrustManager[] { trustManager }, null);
        return sc;
    }

    /**
     * 调用一次远程 api
     * @param url
     * @return
     * @throws IOException
     */
    public static Map<String, Object> post(String url) throws IOException {
        return post(url, new HashMap<>(), false);
    }


    /**
     * 调用一次远程 api
     * @param url
     * @return
     * @throws IOException
     */
    public static Map<String, Object> post(String url, boolean not200ThrowError) throws IOException {
        return post(url, new HashMap<>(), not200ThrowError);
    }


    /**
     * 通过 Post 请求调用Rest 接口
     *
     * @param url
     * @param json
     * @return
     * @throws IOException
     */
    public static Map<String, Object> post(String url, Map<String, Object> json) throws IOException {
        return post(url, json, false);
    }

    /**
     * 通过 Post 请求调用Rest 接口
     * @param url
     * @param json
     * @param not200ThrowError 为 true 时，当返回不是 200，则抛出异常
     * @return
     * @throws IOException
     */
    public static Map<String, Object> post(String url, Map<String, Object> json, boolean not200ThrowError) throws IOException {
        return post(url, JSON.toJSONString(json), not200ThrowError);
    }


    public static Map<String, Object> post(String url, String jsonStr, boolean not200ThrowError) throws IOException {
        HttpPost post = new HttpPost(url);
        StringEntity entity = new StringEntity(jsonStr, "UTF-8");
        entity.setContentType("application/json");
        post.setEntity(entity);
        CloseableHttpResponse resp = null;
        try {
            resp = httpClient.execute(post);
            log.info("execute url {} return code: {}", url, resp.getStatusLine().getStatusCode());
            HttpEntity entity1 = resp.getEntity();
            String result = EntityUtils.toString(entity1);
            EntityUtils.consume(entity1);
            if(not200ThrowError && resp.getStatusLine().getStatusCode() != 200) {
                throw new IOException(result);
            }
            Object jsonResult = JSON.parse(result);
            JSONObject jsonObject = new JSONObject(2);
            if(jsonResult instanceof JSONArray) {
                jsonObject.put("result", jsonResult);
            } else {
                jsonObject = (JSONObject) jsonResult;
            }
            jsonObject.put("status_code", resp.getStatusLine().getStatusCode());
            return jsonObject;
        } finally {
            if(resp != null) {
                resp.close();
            }
        }
    }


    //---------
    /**
     * 调用一次远程 api
     * @param url
     * @return
     * @throws IOException
     */
    public static Map<String, Object> get(String url) throws IOException {
        return get(url, new HashMap<>(), false);
    }


    /**
     * 调用一次远程 api
     * @param url
     * @return
     * @throws IOException
     */
    public static Map<String, Object> get(String url, boolean not200ThrowError) throws IOException {
        return get(url, new HashMap<>(), not200ThrowError);
    }


    /**
     * 通过 Post 请求调用Rest 接口
     *
     * @param url
     * @param json
     * @return
     * @throws IOException
     */
    public static Map<String, Object> get(String url, Map<String, Object> json) throws IOException {
        return get(url, json, false);
    }

    /**
     * 通过 Post 请求调用Rest 接口
     * @param url
     * @param json
     * @param not200ThrowError 为 true 时，当返回不是 200，则抛出异常
     * @return
     * @throws IOException
     */
    public static Map<String, Object> get(String url, Map<String, Object> json, boolean not200ThrowError) throws IOException {
        HttpGet get = new HttpGet(url);
        if(json != null && !json.isEmpty()) {
            BasicHttpParams params = new BasicHttpParams();
            for (Map.Entry<String, Object> entry : json.entrySet()) {
                params.setParameter(entry.getKey(), entry.getValue());
            }
            get.setParams(params);
        }
        CloseableHttpResponse resp = null;
        try {
            resp = httpClient.execute(get);
            log.info("execute url {} return code: {}", url, resp.getStatusLine().getStatusCode());
            HttpEntity entity = resp.getEntity();
            String result = EntityUtils.toString(entity);
            EntityUtils.consume(entity);
            if(not200ThrowError && resp.getStatusLine().getStatusCode() != 200) {
                throw new IOException(result);
            }
            Object jsonResult = JSON.parse(result);
            JSONObject jsonObject = new JSONObject(2);
            if(jsonResult instanceof JSONArray) {
                jsonObject.put("result", jsonResult);
            } else {
                jsonObject = (JSONObject) jsonResult;
            }
            jsonObject.put("status_code", resp.getStatusLine().getStatusCode());
            return jsonObject;
        } finally {
            if(resp != null) {
                resp.close();
            }
        }
    }


    /**
     * 关闭 RPC 调用
     * @throws IOException
     */
    public void shutdown() throws IOException {
        httpClient.close();
    }
}
